/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  					enumerate_tile_points_within_screen_rect.h

	$Header: /heroes4/enumerate_tile_points_within_screen_rect.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( ENUMERATE_TILE_POINTS_WITHIN_SCREEN_RECT_H_INCLUDED )
#define ENUMERATE_TILE_POINTS_WITHIN_SCREEN_RECT_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "abstract_tile.h"
#include "adventure_tile_size.h"
#include "map_point.h"
#include "quad.h"
#include "screen_point.h"

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
template < typename t_terrain_map, typename t_callback >
void enumerate_tile_points_within_screen_rect(
	t_terrain_map			terrain_map,
	int						level,
	t_screen_rect const &	src_rect,
	t_screen_point const &	dest_pos,
	t_callback				callback )
{
	t_screen_point map_view_size = terrain_map.get_view_size();
	t_screen_rect map_view_rect( 0, -k_max_tile_vertex_height, map_view_size.x, map_view_size.y );

	// If the source rect is completly off of the viewable map area, do nothing
	if ( !intersect( src_rect, map_view_rect ) )
		return;

	t_screen_rect adjusted_src_rect = src_rect;
	t_screen_point adjusted_dest_pos = dest_pos;

	if ( adjusted_src_rect.left < map_view_rect.left )
	{
		adjusted_dest_pos.x += map_view_rect.left - adjusted_src_rect.left;
		adjusted_src_rect.left = map_view_rect.left;
	}

	if ( adjusted_src_rect.top < map_view_rect.top )
	{
		adjusted_dest_pos.y += map_view_rect.top - adjusted_src_rect.top;
		adjusted_src_rect.top = map_view_rect.top;
	}

	if ( adjusted_src_rect.right > map_view_rect.right )
		adjusted_src_rect.right = map_view_rect.right;

	if ( adjusted_src_rect.bottom > map_view_rect.bottom )
		adjusted_src_rect.bottom = map_view_rect.bottom;

	t_screen_rect dest_rect( adjusted_dest_pos, adjusted_src_rect.size() );

	int map_size = terrain_map.get_size();
	int map_view_top = ( ( map_size * k_adventure_tile_height ) - map_view_size.y ) / 2;
	int map_view_left = ( ( map_size * k_adventure_tile_width ) - map_view_size.x ) / 2;

	// Compute the map coordinates of the first tile on the first horizontal row of tiles
	// we need to draw.
	t_map_point_2d	row_start(
								(	map_view_top
								+	( adjusted_src_rect.top > 0 ? adjusted_src_rect.top : 0 ) )
							/	( k_adventure_tile_height / 2 )
						-	1,
						0 );

	int row_left = ( map_size - row_start.row - 1 ) * ( k_adventure_tile_width / 2 );
	int row_offset = ( map_view_left + adjusted_src_rect.left - row_left ) / k_adventure_tile_width;
	row_start.row -= row_offset;
	row_start.column += row_offset;

	// Compute the screen coordinates to which to draw the tile
	t_screen_point row_screen_pos = terrain_map.get_screen_point( row_start );
	row_screen_pos += adjusted_dest_pos - adjusted_src_rect.top_left();
	row_screen_pos.x -= k_adventure_tile_width / 2;

	// Draw each row until we've covered the entire clip region
	while ( terrain_map.is_valid( row_start ) )
	{
		// Get the current map coordinates and current screen coordinates
		t_map_point_2d tile_point = row_start;
		t_screen_point screen_pos = row_screen_pos;

		int adjusted_screen_y = screen_pos.y;

		// Draw each tile until we're past the right edge of the clip region
		while ( screen_pos.x < dest_rect.right && terrain_map.is_valid( tile_point ) )
		{
			t_level_map_point_2d level_tile_point( tile_point, level );

			t_quad< t_abstract_tile_vertex const & > vertex_quad = terrain_map.get_tile_vertex_quad( level_tile_point );
			int left_vertex_y = screen_pos.y + k_adventure_tile_height / 2 - vertex_quad.left.get_height();
			int top_vertex_y = screen_pos.y - vertex_quad.top.get_height();
			int right_vertex_y = screen_pos.y + k_adventure_tile_height / 2 - vertex_quad.right.get_height();
			int bottom_vertex_y = screen_pos.y + k_adventure_tile_height - vertex_quad.bottom.get_height();

			if ( left_vertex_y < top_vertex_y )
				top_vertex_y = left_vertex_y;
			else if ( left_vertex_y > bottom_vertex_y )
				bottom_vertex_y = left_vertex_y;

			if ( right_vertex_y < top_vertex_y )
				top_vertex_y = right_vertex_y;
			else if ( right_vertex_y > bottom_vertex_y )
				bottom_vertex_y = right_vertex_y;

			if ( top_vertex_y < adjusted_screen_y )
				adjusted_screen_y = top_vertex_y;

			// Draw the tile
			if ( top_vertex_y < dest_rect.bottom && bottom_vertex_y > dest_rect.top )
				callback( terrain_map, level_tile_point, screen_pos, dest_rect );

			// Advance the map coordinates and screen coordinates for the next tile
			--tile_point.row;
			++tile_point.column;
			screen_pos.x += k_adventure_tile_width;
		}

		if ( adjusted_screen_y >= dest_rect.bottom )
			break;

		// Advance the map coordinates and screen coordinates for the next row
		if ( row_screen_pos.x + k_adventure_tile_width / 2 > dest_rect.left )
		{
			++row_start.row;
			row_screen_pos.x -= k_adventure_tile_width / 2;
		}
		else
		{
			++row_start.column;
			row_screen_pos.x += k_adventure_tile_width / 2;
		}
		row_screen_pos.y += k_adventure_tile_height / 2;
	}
}

#endif // !defined( ENUMERATE_TILE_POINTS_WITHIN_SCREEN_RECT_H_INCLUDED )
